🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / database / src / main / kotlin / org / meshtastic / core / database / DatabaseManager.kt
Displaying Raw • Download
core/database/src/main/kotlin/org/meshtastic/core/database/DatabaseManager.kt 1c3784235e7e20ed88e054097d2e7addbff92dc3 (1c378423) Text, 12.73 KB
T8b949e/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.database
Tff7b72import T7ee787android.app.Application
Tff7b72import T7ee787android.content.Context
Tff7b72import T7ee787android.content.SharedPreferences
Tff7b72import T7ee787androidx.room.Room
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.SupervisorJob
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.SharingStarted
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.filterNotNull
Tff7b72import T7ee787kotlinx.coroutines.flow.stateIn
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787kotlinx.coroutines.sync.Mutex
Tff7b72import T7ee787kotlinx.coroutines.sync.withLock
Tff7b72import T7ee787kotlinx.coroutines.withContext
Tff7b72import T7ee787timber.log.Timber
Tff7b72import T7ee787java.io.File
Tff7b72import T7ee787java.security.MessageDigest
Tff7b72import T7ee787javax.inject.Inject
Tff7b72import T7ee787javax.inject.Singleton
T8b949e/** Manages per-device Room database instances for node data, with LRU eviction. */
Tf0883e@Singleton
Tff7b72class T56d364DatabaseManager Tf0883e@Inject Tff7b72constructorTb4b4b4(Tff7b72private Tff7b72val Te6edf3appTb4b4b4: Te6edf3ApplicationTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3prefsTb4b4b4: Te6edf3SharedPreferences Tff7b72= Te6edf3appTb4b4b4.Te6edf3getSharedPreferencesTb4b4b4(Ta5d6ff"Ta5d6ffdb-manager-prefsTa5d6ff"Tb4b4b4, Te6edf3ContextTb4b4b4.Te6edf3MODE_PRIVATETb4b4b4)
Tff7b72private Tff7b72val Te6edf3managerScope Tff7b72= Te6edf3CoroutineScopeTb4b4b4(Te6edf3SupervisorJobTb4b4b4(Tb4b4b4) Tff7b72+ Te6edf3DispatchersTb4b4b4.Te6edf3DefaultTb4b4b4)
Tff7b72private Tff7b72val Te6edf3mutex Tff7b72= Te6edf3MutexTb4b4b4(Tb4b4b4)
T8b949e// Expose the DB cache limit as a reactive stream so UI can observe changes.
Tff7b72private Tff7b72val Te6edf3_cacheLimit Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3getCacheLimitTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3cacheLimitTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657IntTff7b72> Tff7b72= Te6edf3_cacheLimit
T8b949e// Keep cache-limit StateFlow in sync if some other component updates SharedPreferences.
Tff7b72private Tff7b72val Te6edf3prefsListener Tff7b72=
Te6edf3SharedPreferencesTb4b4b4.Te6edf3OnSharedPreferenceChangeListener Tb4b4b4{ Te6edf3_Tb4b4b4, Te6edf3key Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3key Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3CACHE_LIMIT_KEYTb4b4b4) Tb4b4b4{
Te6edf3_cacheLimitTb4b4b4.Te6edf3value Tff7b72= Te6edf3getCacheLimitTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72init Tb4b4b4{
Te6edf3prefsTb4b4b4.Te6edf3registerOnSharedPreferenceChangeListenerTb4b4b4(Te6edf3prefsListenerTb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72val Te6edf3_currentDb Tff7b72= Te6edf3MutableStateFlowTff7b72<Te6edf3MeshtasticDatabase?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72val Te6edf3currentDbTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3MeshtasticDatabaseTff7b72> Tff7b72=
Te6edf3_currentDbTb4b4b4.Te6edf3filterNotNullTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3stateInTb4b4b4(Te6edf3managerScopeTb4b4b4, Te6edf3SharingStartedTb4b4b4.Te6edf3EagerlyTb4b4b4, Te6edf3buildRoomDbTb4b4b4(Te6edf3appTb4b4b4, Te6edf3defaultDbNameTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3_currentAddress Tff7b72= Te6edf3MutableStateFlowTff7b72<Tffa657String?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72val Te6edf3currentAddressTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657String?Tff7b72> Tff7b72= Te6edf3_currentAddress
Tff7b72private Tff7b72val Te6edf3dbCache Tff7b72= Te6edf3mutableMapOfTff7b72<Tffa657StringTb4b4b4, Te6edf3MeshtasticDatabaseTff7b72>Tb4b4b4(Tb4b4b4) T8b949e// key = dbName
T8b949e/** Initialize the active database for [address]. */
Tff7b72suspend Tff7b72fun Td2a8ffinitTb4b4b4(Te6edf3addressTb4b4b4: Tffa657String?Tb4b4b4) Tff7b72= Te6edf3switchActiveDatabaseTb4b4b4(Te6edf3addressTb4b4b4)
T8b949e/** Switch active database to the one associated with [address]. Serialized via mutex. */
Tff7b72suspend Tff7b72fun Td2a8ffswitchActiveDatabaseTb4b4b4(Te6edf3addressTb4b4b4: Tffa657String?Tb4b4b4) Tff7b72= Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72val Te6edf3dbName Tff7b72= Te6edf3buildDbNameTb4b4b4(Te6edf3addressTb4b4b4)
T8b949e// Remember the previously active DB name (if any) so we can record its last-used time as well.
Tff7b72val Te6edf3previousDbName Tff7b72= Te6edf3_currentDbTb4b4b4.Te6edf3valueTff7b72?.Te6edf3openHelperTff7b72?.Te6edf3databaseName
T8b949e// Fast path: no-op if already on this address
Tff7b72if Tb4b4b4(Te6edf3_currentAddressTb4b4b4.Te6edf3value Tff7b72=Tff7b72= Te6edf3address Tff7b72&Tff7b72& Te6edf3_currentDbTb4b4b4.Te6edf3value Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3markLastUsedTb4b4b4(Te6edf3dbNameTb4b4b4)
Tff7b72return
Tb4b4b4}
T8b949e// Build/open Room DB off the main thread
Tff7b72val Te6edf3db Tff7b72=
Te6edf3dbCacheTff7b72[Te6edf3dbNameTff7b72]
Tff7b72?: Te6edf3withContextTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3IOTb4b4b4) Tb4b4b4{ Te6edf3buildRoomDbTb4b4b4(Te6edf3appTb4b4b4, Te6edf3dbNameTb4b4b4) Tb4b4b4}Tb4b4b4.Te6edf3also Tb4b4b4{ Te6edf3dbCacheTff7b72[Te6edf3dbNameTff7b72] Tff7b72= Tffa657it Tb4b4b4}
Te6edf3_currentDbTb4b4b4.Te6edf3value Tff7b72= Te6edf3db
Te6edf3_currentAddressTb4b4b4.Te6edf3value Tff7b72= Te6edf3address
Te6edf3markLastUsedTb4b4b4(Te6edf3dbNameTb4b4b4)
T8b949e// Also mark the previous DB as used "just now" so LRU has an accurate, recent timestamp
T8b949e// even on first run after upgrade where no timestamp might exist yet.
Te6edf3previousDbNameTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3markLastUsedTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
T8b949e// Defer LRU eviction so switch is not blocked by filesystem work
Te6edf3managerScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3IOTb4b4b4) Tb4b4b4{ Te6edf3enforceCacheLimitTb4b4b4(Te6edf3activeDbName Tff7b72= Te6edf3dbNameTb4b4b4) Tb4b4b4}
T8b949e// One-time cleanup: remove legacy DB if present and not active
Te6edf3managerScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3IOTb4b4b4) Tb4b4b4{ Te6edf3cleanupLegacyDbIfNeededTb4b4b4(Te6edf3activeDbName Tff7b72= Te6edf3dbNameTb4b4b4) Tb4b4b4}
Te6edf3TimberTb4b4b4.Te6edf3iTb4b4b4(Ta5d6ff"Ta5d6ffSwitched active DB to Tffd700${Te6edf3anonymizeDbNameTb4b4b4(Te6edf3dbNameTb4b4b4)Tffd700}Ta5d6ff for address Tffd700${Te6edf3anonymizeAddressTb4b4b4(Te6edf3addressTb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
T8b949e/** Execute [block] with the current DB instance. */
Tff7b72fun Tff7b72<Te6edf3TTff7b72> Td2a8ffwithDbTb4b4b4(Te6edf3blockTb4b4b4: Tb4b4b4(Te6edf3MeshtasticDatabaseTb4b4b4) Tff7b72-Tff7b72> Te6edf3TTb4b4b4)Tb4b4b4: Te6edf3T Tff7b72= Te6edf3blockTb4b4b4(Te6edf3currentDbTb4b4b4.Te6edf3valueTb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffmarkLastUsedTb4b4b4(Te6edf3dbNameTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4{
Te6edf3prefsTb4b4b4.Te6edf3editTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3putLongTb4b4b4(Te6edf3lastUsedKeyTb4b4b4(Te6edf3dbNameTb4b4b4)Tb4b4b4, Te6edf3SystemTb4b4b4.Te6edf3currentTimeMillisTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3applyTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8fflastUsedTb4b4b4(Te6edf3dbNameTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Long Tb4b4b4{
Tff7b72val Te6edf3k Tff7b72= Te6edf3lastUsedKeyTb4b4b4(Te6edf3dbNameTb4b4b4)
Tff7b72val Te6edf3v Tff7b72= Te6edf3prefsTb4b4b4.Te6edf3getLongTb4b4b4(Te6edf3kTb4b4b4, T79c0ff0LTb4b4b4)
Tff7b72return Tff7b72if Tb4b4b4(Te6edf3v Tff7b72=Tff7b72= T79c0ff0LTb4b4b4) Te6edf3getDbFileTb4b4b4(Te6edf3appTb4b4b4, Te6edf3dbNameTb4b4b4)Tff7b72?.Te6edf3lastModifiedTb4b4b4(Tb4b4b4) Tff7b72?: T79c0ff0L Tff7b72else Te6edf3v
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8fflistExistingDbNamesTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Tffa657StringTff7b72> Tb4b4b4{
Tff7b72val Te6edf3base Tff7b72= Te6edf3appTb4b4b4.Te6edf3getDatabasePathTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_NAMETb4b4b4)
Tff7b72val Te6edf3dir Tff7b72= Te6edf3baseTb4b4b4.Te6edf3parentFile Tff7b72?: Tff7b72return Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3names Tff7b72= Te6edf3dirTb4b4b4.Te6edf3listFilesTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3mapNotNull Tb4b4b4{ Te6edf3f Tff7b72-Tff7b72> Te6edf3fTb4b4b4.Te6edf3name Tb4b4b4} Tff7b72?: Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3names
Tb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3startsWithTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DB_PREFIXTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3filterNot Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3endsWithTb4b4b4(Ta5d6ff"Ta5d6ff-walTa5d6ff"Tb4b4b4) Tff7b72|Tff7b72| Tffa657itTb4b4b4.Te6edf3endsWithTb4b4b4(Ta5d6ff"Ta5d6ff-shmTa5d6ff"Tb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3distinctTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffenforceCacheLimitTb4b4b4(Te6edf3activeDbNameTb4b4b4: Tffa657StringTb4b4b4) Tff7b72= Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72val Te6edf3limit Tff7b72= Te6edf3getCacheLimitTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3all Tff7b72= Te6edf3listExistingDbNamesTb4b4b4(Tb4b4b4)
T8b949e// Only enforce the limit over device-specific DBs; exclude legacy and default DBs
Tff7b72val Te6edf3deviceDbs Tff7b72=
Te6edf3allTb4b4b4.Te6edf3filterNot Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_NAME Tff7b72|Tff7b72| Tffa657it Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DEFAULT_DB_NAME Tb4b4b4}
Te6edf3TimberTb4b4b4.Te6edf3dTb4b4b4(
Ta5d6ff"Ta5d6ffLRU check: limit=%d, active=%s, deviceDbs=%sTa5d6ff"Tb4b4b4,
Te6edf3limitTb4b4b4,
Te6edf3anonymizeDbNameTb4b4b4(Te6edf3activeDbNameTb4b4b4)Tb4b4b4,
Te6edf3deviceDbsTb4b4b4.Te6edf3joinToStringTb4b4b4(Ta5d6ff"Ta5d6ff, Ta5d6ff"Tb4b4b4) Tb4b4b4{ Te6edf3anonymizeDbNameTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3deviceDbsTb4b4b4.Te6edf3size Tff7b72<Tff7b72= Te6edf3limitTb4b4b4) Tff7b72return
Tff7b72val Te6edf3usageSnapshot Tff7b72= Te6edf3deviceDbsTb4b4b4.Te6edf3associateWith Tb4b4b4{ Te6edf3lastUsedTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Te6edf3TimberTb4b4b4.Te6edf3dTb4b4b4(
Ta5d6ff"Ta5d6ffLRU lastUsed(ms): %sTa5d6ff"Tb4b4b4,
Te6edf3usageSnapshotTb4b4b4.Te6edf3entriesTb4b4b4.Te6edf3joinToStringTb4b4b4(Ta5d6ff"Ta5d6ff, Ta5d6ff"Tb4b4b4) Tb4b4b4{ Tb4b4b4(Te6edf3nameTb4b4b4, Te6edf3tsTb4b4b4) Tff7b72-Tff7b72> Ta5d6ff"Tffd700${Te6edf3anonymizeDbNameTb4b4b4(Te6edf3nameTb4b4b4)Tffd700}Ta5d6ff=Tffd700$Te6edf3tsTa5d6ff" Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3victims Tff7b72= Te6edf3selectEvictionVictimsTb4b4b4(Te6edf3deviceDbsTb4b4b4, Te6edf3activeDbNameTb4b4b4, Te6edf3limitTb4b4b4, Te6edf3usageSnapshotTb4b4b4)
Te6edf3TimberTb4b4b4.Te6edf3iTb4b4b4(Ta5d6ff"Ta5d6ffLRU victims: %sTa5d6ff"Tb4b4b4, Te6edf3victimsTb4b4b4.Te6edf3joinToStringTb4b4b4(Ta5d6ff"Ta5d6ff, Ta5d6ff"Tb4b4b4) Tb4b4b4{ Te6edf3anonymizeDbNameTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}Tb4b4b4)
Te6edf3victimsTb4b4b4.Te6edf3forEach Tb4b4b4{ Te6edf3name Tff7b72-Tff7b72>
Te6edf3runCatching Tb4b4b4{ Te6edf3dbCacheTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3nameTb4b4b4)Tff7b72?.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3onFailure Tb4b4b4{ Te6edf3TimberTb4b4b4.Te6edf3wTb4b4b4(Tffa657itTb4b4b4, Ta5d6ff"Ta5d6ffFailed to close database %sTa5d6ff"Tb4b4b4, Te6edf3nameTb4b4b4) Tb4b4b4}
Te6edf3appTb4b4b4.Te6edf3deleteDatabaseTb4b4b4(Te6edf3nameTb4b4b4)
Te6edf3prefsTb4b4b4.Te6edf3editTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3removeTb4b4b4(Te6edf3lastUsedKeyTb4b4b4(Te6edf3nameTb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3applyTb4b4b4(Tb4b4b4)
Te6edf3TimberTb4b4b4.Te6edf3iTb4b4b4(Ta5d6ff"Ta5d6ffEvicted cached DB Tffd700${Te6edf3anonymizeDbNameTb4b4b4(Te6edf3nameTb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72fun Td2a8ffgetCacheLimitTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Int Tff7b72= Te6edf3prefs
Tb4b4b4.Te6edf3getIntTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3CACHE_LIMIT_KEYTb4b4b4, Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DEFAULT_CACHE_LIMITTb4b4b4)
Tb4b4b4.Te6edf3coerceInTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3MIN_CACHE_LIMITTb4b4b4, Te6edf3DatabaseConstantsTb4b4b4.Te6edf3MAX_CACHE_LIMITTb4b4b4)
Tff7b72fun Td2a8ffsetCacheLimitTb4b4b4(Te6edf3limitTb4b4b4: Tffa657IntTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3clamped Tff7b72= Te6edf3limitTb4b4b4.Te6edf3coerceInTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3MIN_CACHE_LIMITTb4b4b4, Te6edf3DatabaseConstantsTb4b4b4.Te6edf3MAX_CACHE_LIMITTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3clamped Tff7b72=Tff7b72= Te6edf3getCacheLimitTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72return
Te6edf3prefsTb4b4b4.Te6edf3editTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3putIntTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3CACHE_LIMIT_KEYTb4b4b4, Te6edf3clampedTb4b4b4)Tb4b4b4.Te6edf3applyTb4b4b4(Tb4b4b4)
Te6edf3_cacheLimitTb4b4b4.Te6edf3value Tff7b72= Te6edf3clamped
T8b949e// Enforce asynchronously with current active DB protected
Tff7b72val Te6edf3active Tff7b72= Te6edf3_currentDbTb4b4b4.Te6edf3valueTff7b72?.Te6edf3openHelperTff7b72?.Te6edf3databaseName Tff7b72?: Te6edf3defaultDbNameTb4b4b4(Tb4b4b4)
Te6edf3managerScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3IOTb4b4b4) Tb4b4b4{ Te6edf3enforceCacheLimitTb4b4b4(Te6edf3activeDbName Tff7b72= Te6edf3activeTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffcleanupLegacyDbIfNeededTb4b4b4(Te6edf3activeDbNameTb4b4b4: Tffa657StringTb4b4b4) Tff7b72= Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3prefsTb4b4b4.Te6edf3getBooleanTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_CLEANED_KEYTb4b4b4, Tff7b72falseTb4b4b4)Tb4b4b4) Tff7b72return
Tff7b72val Te6edf3legacy Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_NAME
Tff7b72if Tb4b4b4(Te6edf3legacy Tff7b72=Tff7b72= Te6edf3activeDbNameTb4b4b4) Tb4b4b4{
T8b949e// Never delete the active DB; mark as cleaned to avoid repeated checks
Te6edf3prefsTb4b4b4.Te6edf3editTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3putBooleanTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_CLEANED_KEYTb4b4b4, Tff7b72trueTb4b4b4)Tb4b4b4.Te6edf3applyTb4b4b4(Tb4b4b4)
Tff7b72return
Tb4b4b4}
Tff7b72val Te6edf3legacyFile Tff7b72= Te6edf3getDbFileTb4b4b4(Te6edf3appTb4b4b4, Te6edf3legacyTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3legacyFile Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3runCatching Tb4b4b4{ Te6edf3dbCacheTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3legacyTb4b4b4)Tff7b72?.Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3onFailure Tb4b4b4{ Te6edf3TimberTb4b4b4.Te6edf3wTb4b4b4(Tffa657itTb4b4b4, Ta5d6ff"Ta5d6ffFailed to close legacy database %s before deletionTa5d6ff"Tb4b4b4, Te6edf3legacyTb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3deleted Tff7b72= Te6edf3appTb4b4b4.Te6edf3deleteDatabaseTb4b4b4(Te6edf3legacyTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3deletedTb4b4b4) Tb4b4b4{
Te6edf3TimberTb4b4b4.Te6edf3iTb4b4b4(Ta5d6ff"Ta5d6ffDeleted legacy DB Tffd700${Te6edf3anonymizeDbNameTb4b4b4(Te6edf3legacyTb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3TimberTb4b4b4.Te6edf3wTb4b4b4(Ta5d6ff"Ta5d6ffAttempted to delete legacy DB %s but deleteDatabase returned falseTa5d6ff"Tb4b4b4, Te6edf3legacyTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Te6edf3prefsTb4b4b4.Te6edf3editTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3putBooleanTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_CLEANED_KEYTb4b4b4, Tff7b72trueTb4b4b4)Tb4b4b4.Te6edf3applyTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72object T56d364DatabaseConstants Tb4b4b4{
Tff7b72const Tff7b72val Te6edf3DB_PREFIXTb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Ta5d6ffmeshtastic_databaseTa5d6ff"
Tff7b72const Tff7b72val Te6edf3LEGACY_DB_NAMETb4b4b4: Tffa657String Tff7b72= Te6edf3DB_PREFIX
Tff7b72const Tff7b72val Te6edf3DEFAULT_DB_NAMETb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Tffd700${Te6edf3DB_PREFIXTffd700}Ta5d6ff_defaultTa5d6ff"
Tff7b72const Tff7b72val Te6edf3CACHE_LIMIT_KEYTb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Ta5d6ffnode_db_cache_limitTa5d6ff"
Tff7b72const Tff7b72val Te6edf3DEFAULT_CACHE_LIMITTb4b4b4: Tffa657Int Tff7b72= T79c0ff3
Tff7b72const Tff7b72val Te6edf3MIN_CACHE_LIMITTb4b4b4: Tffa657Int Tff7b72= T79c0ff1
Tff7b72const Tff7b72val Te6edf3MAX_CACHE_LIMITTb4b4b4: Tffa657Int Tff7b72= T79c0ff1T79c0ff0
Tff7b72const Tff7b72val Te6edf3LEGACY_DB_CLEANED_KEYTb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Ta5d6fflegacy_db_cleanedTa5d6ff"
T8b949e// Display/truncation and hash sizing for DB names
Tff7b72const Tff7b72val Te6edf3DB_NAME_HASH_LENTb4b4b4: Tffa657Int Tff7b72= T79c0ff1T79c0ff0
Tff7b72const Tff7b72val Te6edf3DB_NAME_SEPARATOR_LENTb4b4b4: Tffa657Int Tff7b72= T79c0ff1
Tff7b72const Tff7b72val Te6edf3DB_NAME_SUFFIX_LENTb4b4b4: Tffa657Int Tff7b72= T79c0ff3
T8b949e// Address anonymization sizing
Tff7b72const Tff7b72val Te6edf3ADDRESS_ANON_SHORT_LENTb4b4b4: Tffa657Int Tff7b72= T79c0ff4
Tff7b72const Tff7b72val Te6edf3ADDRESS_ANON_EDGE_LENTb4b4b4: Tffa657Int Tff7b72= T79c0ff2
Tb4b4b4}
T8b949e// File-private helpers (kept outside the class to reduce class function count)
Tff7b72private Tff7b72fun Td2a8ffdefaultDbNameTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DEFAULT_DB_NAME
Tff7b72private Tff7b72fun Td2a8ffnormalizeAddressTb4b4b4(Te6edf3addrTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657String Tb4b4b4{
Tff7b72val Te6edf3u Tff7b72= Te6edf3addrTff7b72?.Te6edf3trimTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3uppercaseTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3normalized Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3uTb4b4b4.Te6edf3isNullOrBlankTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffDEFAULTTa5d6ff"
Te6edf3u Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffNTa5d6ff" Tff7b72|Tff7b72| Te6edf3u Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffNULLTa5d6ff" Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffDEFAULTTa5d6ff"
Tff7b72else Tff7b72-Tff7b72> Te6edf3uTb4b4b4.Te6edf3replaceTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3normalized
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffshortSha1Tb4b4b4(Te6edf3sTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Te6edf3MessageDigestTb4b4b4.Te6edf3getInstanceTb4b4b4(Ta5d6ff"Ta5d6ffSHA-1Ta5d6ff"Tb4b4b4)
Tb4b4b4.Te6edf3digestTb4b4b4(Te6edf3sTb4b4b4.Te6edf3toByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4.Te6edf3joinToStringTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ff%02xTa5d6ff"Tb4b4b4.Te6edf3formatTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3takeTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DB_NAME_HASH_LENTb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffbuildDbNameTb4b4b4(Te6edf3addressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Tff7b72if Tb4b4b4(Te6edf3addressTb4b4b4.Te6edf3isNullOrBlankTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3defaultDbNameTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Ta5d6ff"Tffd700${Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DB_PREFIXTffd700}Ta5d6ff_Tffd700${Te6edf3shortSha1Tb4b4b4(Te6edf3normalizeAddressTb4b4b4(Te6edf3addressTb4b4b4)Tb4b4b4)Tffd700}Ta5d6ff"
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8fflastUsedKeyTb4b4b4(Te6edf3dbNameTb4b4b4: Tffa657StringTb4b4b4) Tff7b72= Ta5d6ff"Ta5d6ffdb_last_used:Tffd700$Te6edf3dbNameTa5d6ff"
Tff7b72private Tff7b72fun Td2a8ffanonymizeAddressTb4b4b4(Te6edf3addressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Tff7b72when Tb4b4b4{
Te6edf3address Tff7b72=Tff7b72= Tff7b72null Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffnullTa5d6ff"
Te6edf3addressTb4b4b4.Te6edf3length Tff7b72<Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3ADDRESS_ANON_SHORT_LEN Tff7b72-Tff7b72> Te6edf3address
Tff7b72else Tff7b72-Tff7b72>
Te6edf3addressTb4b4b4.Te6edf3takeTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3ADDRESS_ANON_EDGE_LENTb4b4b4) Tff7b72+
Ta5d6ff"Ta5d6ff…Ta5d6ff" Tff7b72+
Te6edf3addressTb4b4b4.Te6edf3takeLastTb4b4b4(Te6edf3DatabaseConstantsTb4b4b4.Te6edf3ADDRESS_ANON_EDGE_LENTb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffanonymizeDbNameTb4b4b4(Te6edf3nameTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657String Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3name Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_NAME Tff7b72|Tff7b72| Te6edf3name Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DEFAULT_DB_NAMETb4b4b4) Tb4b4b4{
Te6edf3name
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3nameTb4b4b4.Te6edf3takeTb4b4b4(
Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DB_PREFIXTb4b4b4.Te6edf3length Tff7b72+
Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DB_NAME_SEPARATOR_LEN Tff7b72+
Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DB_NAME_SUFFIX_LENTb4b4b4,
Tb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff…Ta5d6ff"
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffbuildRoomDbTb4b4b4(Te6edf3appTb4b4b4: Te6edf3ApplicationTb4b4b4, Te6edf3dbNameTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3MeshtasticDatabase Tff7b72=
Te6edf3RoomTb4b4b4.Te6edf3databaseBuilderTb4b4b4(Te6edf3appTb4b4b4.Te6edf3applicationContextTb4b4b4, Te6edf3MeshtasticDatabaseTff7b72::Te6edf3classTb4b4b4.Te6edf3javaTb4b4b4, Te6edf3dbNameTb4b4b4)
Tb4b4b4.Te6edf3fallbackToDestructiveMigrationTb4b4b4(Tff7b72falseTb4b4b4)
Tb4b4b4.Te6edf3buildTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffgetDbFileTb4b4b4(Te6edf3appTb4b4b4: Te6edf3ApplicationTb4b4b4, Te6edf3dbNameTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3File? Tff7b72= Te6edf3appTb4b4b4.Te6edf3getDatabasePathTb4b4b4(Te6edf3dbNameTb4b4b4)Tb4b4b4.Te6edf3takeIf Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3existsTb4b4b4(Tb4b4b4) Tb4b4b4}
T8b949e/**
* Compute which DBs to evict using LRU policy.
*
* Rules:
* - Only consider device-specific DBs (exclude legacy and default)
* - Never evict the active DB
* - If number of device DBs is within the limit, evict none
* - Otherwise evict the (size - limit) least-recently-used DBs
*
* Pass a precomputed [lastUsedMsByDb] snapshot to avoid redundant IO/lookups.
*/
Tff7b72internal Tff7b72fun Td2a8ffselectEvictionVictimsTb4b4b4(
Te6edf3dbNamesTb4b4b4: Te6edf3ListTff7b72<Tffa657StringTff7b72>Tb4b4b4,
Te6edf3activeDbNameTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3limitTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3lastUsedMsByDbTb4b4b4: Te6edf3MapTff7b72<Tffa657StringTb4b4b4, Tffa657LongTff7b72>Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Tffa657StringTff7b72> Tb4b4b4{
Tff7b72val Te6edf3deviceDbNames Tff7b72=
Te6edf3dbNamesTb4b4b4.Te6edf3filterNot Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3LEGACY_DB_NAME Tff7b72|Tff7b72| Tffa657it Tff7b72=Tff7b72= Te6edf3DatabaseConstantsTb4b4b4.Te6edf3DEFAULT_DB_NAME Tb4b4b4}
Tff7b72val Te6edf3victims Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3limit Tff7b72< T79c0ff1 Tff7b72|Tff7b72| Te6edf3deviceDbNamesTb4b4b4.Te6edf3size Tff7b72<Tff7b72= Te6edf3limitTb4b4b4) Tb4b4b4{
Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3candidates Tff7b72= Te6edf3deviceDbNamesTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657it Tff7b72!Tff7b72= Te6edf3activeDbName Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3candidatesTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3toEvict Tff7b72= Te6edf3deviceDbNamesTb4b4b4.Te6edf3size Tff7b72- Te6edf3limit
Te6edf3candidatesTb4b4b4.Te6edf3sortedBy Tb4b4b4{ Te6edf3lastUsedMsByDbTff7b72[Tffa657itTff7b72] Tff7b72?: T79c0ff0L Tb4b4b4}Tb4b4b4.Te6edf3takeTb4b4b4(Te6edf3toEvictTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3victims
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.17s